var supportTouch = ('ontouchstart' in window);
var simulatorEventtype = ['mousedown', 'mousemove', 'mouseup', 'mouseup'];
$.each('touchstart touchmove touchend touchcancel'.split(" "), function(i, name) {
	$.event.special[name] = {
		setup: function() {
			if (supportTouch) return false;
			var elem = $(this);
			elem.bind(simulatorEventtype[i], touch_handler);
		},
		teardown: function() {
			if (supportTouch) return false;
			var elem = $(this);
			elem.unbind(simulatorEventtype[i], touch_handler);
		}
	};

	function touch_handler(e) {
		if (e.which == 3) {
			return;
		}
		var touchsEvent = [{
			target:e.target,
			pageX: e.pageX,
			pageY: e.pageY
		}];
		e.touches = touchsEvent;
		e.changedTouches = touchsEvent;
		e.originalEvent = {
			touches: touchsEvent,
			changedTouches: touchsEvent,
			pageX: e.pageX,
			pageY: e.pageY
		};
		e.type = name;
		$(e.currentTarget).trigger(e);
	}
});


var touch = {},
  touchTimeout

function parentIfText(node) {
  return 'tagName' in node ? node : node.parentNode
}

function swipeDirection(x1, x2, y1, y2) {
  var xDelta = Math.abs(x1 - x2),
    yDelta = Math.abs(y1 - y2)
    return xDelta >= yDelta ? (x1 - x2 > 0 ? 'Left' : 'Right') : (y1 - y2 > 0 ? 'Up' : 'Down')
}

var longTapDelay = 750,
  longTapTimeout

function longTap() {
  longTapTimeout = null
  if (touch.last) {
    touch.el.trigger('longTap')
    touch = {}
  }
}

function cancelLongTap() {
  if (longTapTimeout) clearTimeout(longTapTimeout)
  longTapTimeout = null
}

$(document).ready(function() {
  var now, delta

  $(document.body).bind('touchstart', function(e) {
    now = Date.now()
    delta = now - (touch.last || now)
    touch.el = $(parentIfText(e.originalEvent.touches[0].target))
    touchTimeout && clearTimeout(touchTimeout)
    touch.x1 = e.originalEvent.touches[0].pageX
    touch.y1 = e.originalEvent.touches[0].pageY
    if (delta > 0 && delta <= 250) touch.isDoubleTap = true
    touch.last = now
    longTapTimeout = setTimeout(longTap, longTapDelay)
  }).bind('touchmove', function(e) {
    cancelLongTap()
    touch.x2 = e.originalEvent.touches[0].pageX
    touch.y2 = e.originalEvent.touches[0].pageY
  }).bind('touchend', function(e) {
    cancelLongTap()

    // double tap (tapped twice within 250ms)
    if (touch.isDoubleTap) {
      touch.el.trigger('doubleTap')
      touch = {}

      // swipe
    } else if ((touch.x2 && Math.abs(touch.x1 - touch.x2) > 30) || (touch.y2 && Math.abs(touch.y1 - touch.y2) > 30)) {
      touch.el.trigger('swipe',swipeDirection(touch.x1, touch.x2, touch.y1, touch.y2)) && touch.el.trigger('swipe' + (swipeDirection(touch.x1, touch.x2, touch.y1, touch.y2)))
      touch = {}

      // normal tap
    } else if ('last' in touch) {
      touch.el.trigger('tap')

      touchTimeout = setTimeout(function() {
        touchTimeout = null
        touch.el.trigger('singleTap')
        touch = {}
      }, 250)
    }
  }).bind('touchcancel', function() {
    if (touchTimeout) clearTimeout(touchTimeout)
    if (longTapTimeout) clearTimeout(longTapTimeout)
    longTapTimeout = touchTimeout = null
    touch = {}
  })
});

['swipe', 'swipeLeft', 'swipeRight', 'swipeUp', 'swipeDown', 'doubleTap', 'tap', 'singleTap', 'longTap'].forEach(function(m) {
  $.fn[m] = function(callback) {
    return this.bind(m, callback)
  }
})